import type { ColumnType, CellEventParams } from './types'
import type { CellRendererParams, RowRendererParams } from '../grid/types'

import clsx from 'clsx'
import { ref, provide, defineComponent, getCurrentInstance } from 'vue'
import { tableProps } from './props'
import { TABLE_SLOT_KEY } from './context'
import useTable from './hooks/useTable'
import useColumns from './hooks/useColumns'
import useResize from './hooks/useResize'
import VariableSizeGrid from '../grid/VariableSizeGrid'
import HeaderCellRenderer from './renderers/HeaderCellRenderer'
import BodyCellRenderer from './renderers/BodyCellRenderer'
import { Empty } from 'ant-design-vue'

export default defineComponent({
  name: 'VirtualizedTable',
  props: tableProps,
  emits: [
    'cell-click',
    'row-click',
    'column-click',
    'cell-dblclick',
    'row-dblclick',
    'column-dblclick',
    'selection-change'
  ],
  setup(props, { emit, slots, expose }) {
    provide(TABLE_SLOT_KEY, slots)
    const virtualizedRef = ref()
    const instance = getCurrentInstance()
    const hoveringIndex = ref<number>(-1)

    const { columnsWidthRef, getColumns, getLeftColumns, getRightColumns, getFlattenedColumns } =
      useColumns(props)
    const { getCellKey, getHeaderCellKey, getColumnWidth, getRowHeight, getHeaderRowHeight } =
      useTable(props, getColumns, getFlattenedColumns, columnsWidthRef)

    const { resizeProxyVisible, handleMousedown, handleMousemove, handleMouseout } = useResize(
      props,
      instance,
      columnsWidthRef
    )

    function _rowRenderer(params: RowRendererParams) {
      const { rowClassName, dataSource } = props
      const { rowIndex, style, cells, key, isHeader = false } = params
      let className = ''
      if (isHeader) {
        className = clsx('virtualized-grid-row')
      } else {
        const cls =
          typeof rowClassName === 'function' && rowClassName(dataSource[rowIndex], rowIndex)
        className = clsx('virtualized-grid-row', cls, {
          'is-odd': rowIndex % 2 === 1,
          'is-even': rowIndex % 2 === 0,
          'is-hover': rowIndex === hoveringIndex.value
        })
      }
      return (
        <div
          aria-rowindex={rowIndex}
          key={key}
          class={className}
          style={style}
          role="row"
          onMouseenter={(e: MouseEvent) => mouseenterHandler(e, rowIndex)}
          onMouseleave={mouseleaveHandler}
        >
          {cells}
        </div>
      )
    }

    function _headerCellRenderer(params: CellRendererParams, columns: ColumnType[]) {
      const { columnIndex, style, key } = params
      const column = columns[columnIndex]
      const className = clsx({ 'is-last-col': columnIndex === columns.length - 1 })
      if (!column.flat) {
        return (
          <HeaderCellRenderer
            key={key}
            column={column}
            columnIndex={columnIndex}
            className={className}
            style={style}
            mousedownHandler={handleMousedown}
            mousemoveHandler={handleMousemove}
            mouseoutHandler={handleMouseout}
          />
        )
      } else {
        if (column.flatRender) {
          const parent = getColumns.value.find((col) => col.field === column.flat)
          if (parent) {
            return (
              <HeaderCellRenderer
                key={key}
                column={parent}
                columnIndex={columnIndex}
                className={className}
                style={style}
                mousedownHandler={handleMousedown}
                mousemoveHandler={handleMousemove}
                mouseoutHandler={handleMouseout}
              />
            )
          }
        }
      }
      return null
    }

    function _bodyCellRenderer(params: CellRendererParams, columns: ColumnType[]) {
      const { rowIndex, columnIndex, style, key } = params
      const { dataSource } = props
      const record = dataSource[rowIndex]
      const column = columns[columnIndex]
      const className = clsx({
        'is-last-col': columnIndex === columns.length - 1
      })
      return (
        <BodyCellRenderer
          record={record}
          column={column}
          rowIndex={rowIndex}
          columnIndex={columnIndex}
          className={className}
          style={style}
          key={key}
          clickHandler={clickHandler}
          dblclickHandler={dblclickHandler}
        />
      )
    }

    function clickHandler(params: CellEventParams) {
      emit('cell-click', params)
      emit('row-click', { record: params.record, rowIndex: params.rowIndex })
      emit('column-click', { column: params.column, columnIndex: params.columnIndex })
    }

    function dblclickHandler(params: CellEventParams) {
      emit('cell-dblclick', params)
      emit('row-dblclick', { record: params.record, rowIndex: params.rowIndex })
      emit('column-dblclick', { column: params.column, columnIndex: params.columnIndex })
    }

    function mouseenterHandler(e: MouseEvent, index: number) {
      const body = (e.target as HTMLElement).closest('.virtualized-grid-body')
      if (body) {
        hoveringIndex.value = index
      }
    }
    function mouseleaveHandler() {
      hoveringIndex.value = -1
    }

    function resetMeasuredIndices() {
      if (virtualizedRef.value) {
        virtualizedRef.value.resetMeasuredIndicesAfterChange('column')
      }
    }

    expose({
      resetMeasuredIndices
    })

    return () => {
      const {
        border,
        stripe,
        width,
        height,
        estimatedColumnWidth,
        estimatedRowHeight,
        overscanRowCount,
        overscanColumnCount,
        dataSource
      } = props

      const headerHeight = getHeaderRowHeight()

      return (
        <div class={clsx('virtualized', { 'is-border': border, 'is-stripe': stripe })}>
          <VariableSizeGrid
            ref={virtualizedRef}
            width={width}
            height={height}
            columnCount={getFlattenedColumns.value.length}
            rowCount={dataSource.length}
            columnWidth={getColumnWidth}
            headerHeight={headerHeight}
            rowHeight={getRowHeight}
            estimatedColumnWidth={estimatedColumnWidth}
            estimatedRowHeight={estimatedRowHeight}
            overscanColumnCount={overscanColumnCount}
            overscanRowCount={overscanRowCount}
            cellKey={getCellKey}
            headerCellKey={getHeaderCellKey}
            rowRenderer={(params) => _rowRenderer(params)}
            cellRenderer={(params) => _bodyCellRenderer(params, getFlattenedColumns.value)}
            headerCellRenderer={(params) => _headerCellRenderer(params, getFlattenedColumns.value)}
            leftColumnCount={getLeftColumns.value.length}
            rightColumnCount={getRightColumns.value.length}
          >
            {{
              empty: () => <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
            }}
          </VariableSizeGrid>
          <div
            id="resize-proxy"
            class="virtualized-column-resize-proxy"
            style={{ display: resizeProxyVisible.value ? 'block' : 'none' }}
          ></div>
        </div>
      )
    }
  }
})
